package com.zj.esproxy.wrapper;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

import java.util.*;
import java.util.function.Consumer;

/**
 * es filter 拼接
 *
 * @author arthur_zhou
 * @date 2024/4/10 15:22
 */
public class EsQueryWrapper {

    private static final String TERM = "{\"term\": {\"%s\": %s}}";
    private static final String TERMS = "{\"terms\": {\"%s\": %s}}";
    private static final String RANGE = "{\"range\": {\"%s\": %s}}";
    private static final String EXISTS = "{\"exists\": {\"field\": \"%s\"}}";
    private static final String DISTANCE_RANGE = "{\"distance_range\":{\"%s\":{\"from\":%s,\"to\":%s,\"lon\":%s,\"lat\":%s}}}";
    private static final String GRAPH = "{\"graph\": {\"field\": \"%s\",\"points\": %s}}";

    private static final String AND = "{\"and\":[%s]}";
    private static final String OR = "{\"or\":[%s]}";
    private static final String NOT = "{\"not\":[%s]}";

    private static final String EMPTY = "";
    private static final String STR_EMPTY = "\"\"";
    private static final String ARRAY_EMPTY = "[]";
    private static final String JSON_EMPTY = "{\"and\":[]}";

    private static class Range {
        private final Object gt;
        private final Object gte;
        private final Object lt;
        private final Object lte;

        public Range(Object gt, Object gte, Object lt, Object lte) {
            this.gt = gt;
            this.gte = gte;
            this.lt = lt;
            this.lte = lte;
        }

        public Object getGt() {
            return gt;
        }

        public Object getGte() {
            return gte;
        }

        public Object getLt() {
            return lt;
        }

        public Object getLte() {
            return lte;
        }

        public boolean nonNull() {
            return isNotEmptyStr(gt) || isNotEmptyStr(gte) || isNotEmptyStr(lt) || isNotEmptyStr(lte);
        }

        @Override
        public String toString() {
            return JSON.toJSONString(this);
        }
    }

    public static class Point {
        private final Object lon;
        private final Object lat;

        public Object getLon() {
            return lon;
        }

        public Object getLat() {
            return lat;
        }

        public Point(Object lon, Object lat) {
            this.lon = lon;
            this.lat = lat;
        }
    }

    /**
     * 关系枚举
     */
    private enum RelationEnum {
        /**
         * and
         */
        AND,
        /**
         * or
         */
        OR,
        /**
         * not
         */
        NOT
    }

    private RelationEnum relationEnum = RelationEnum.AND;

    /**
     * 条件数组
     */
    private final List<String> queryList;

    public EsQueryWrapper() {
        this.queryList = new ArrayList<>();
    }

    public EsQueryWrapper(EsQueryWrapper wrapper) {
        this.queryList = new ArrayList<>(wrapper.getQueryList());
    }

    public EsQueryWrapper(List<String> queryList) {
        this.queryList = queryList;
    }

    /**
     * 等于
     *
     * @param column 查询字段
     * @param val    值
     * @return 自己
     */
    public EsQueryWrapper term(String column, Object val) {
        String s = replaceStr(val);
        if (isNotEmptyStr(s)) {
            format(TERM, column, s);
        }
        return this;
    }

    /**
     * 等于 空字符串
     *
     * @param column 查询字段
     * @return 自己
     */
    public EsQueryWrapper termEmpty(String column) {
        format(TERM, column, STR_EMPTY);
        return this;
    }

    /**
     * in
     *
     * @param column 查询字段
     * @param val    数组
     * @return 自己
     */
    public EsQueryWrapper terms(String column, Object... val) {
        return terms(column, Objects.nonNull(val) ? Arrays.asList(val) : null);
    }

    /**
     * in
     *
     * @param column 查询字段
     * @param val    数组
     * @return 自己
     */
    public EsQueryWrapper terms(String column, Collection<?> val) {
        String s = replaceStr(val);
        if (isNotEmptyStr(s)) {
            format(TERMS, column, s);
        }
        return this;
    }

    /**
     * 范围
     *
     * @param column 查询字段
     * @param gt     大于
     * @return 自己
     */
    public EsQueryWrapper gt(String column, Object gt) {
        return range(column, gt, null, null, null);
    }

    /**
     * 范围
     *
     * @param column 查询字段
     * @param gte    大于等于
     * @return 自己
     */
    public EsQueryWrapper ge(String column, Object gte) {
        return range(column, null, gte, null, null);
    }

    /**
     * 范围
     *
     * @param column 查询字段
     * @param lt     小于
     * @return 自己
     */
    public EsQueryWrapper lt(String column, Object lt) {
        return range(column, null, null, lt, null);
    }

    /**
     * 范围
     *
     * @param column 查询字段
     * @param lte    小于等于
     * @return 自己
     */
    public EsQueryWrapper le(String column, Object lte) {
        return range(column, null, null, null, lte);
    }

    /**
     * 范围
     *
     * @param column 查询字段
     * @param gte    大于等于
     * @param lte    小于等于
     * @return 自己
     */
    public EsQueryWrapper between(String column, Object gte, Object lte) {
        return range(column, null, gte, null, lte);
    }

    /**
     * 范围
     *
     * @param column 查询字段
     * @param gt     大于
     * @param gte    大于等于
     * @param lt     小于
     * @param lte    小于等于
     * @return 自己
     */
    public EsQueryWrapper range(String column, Object gt, Object gte, Object lt, Object lte) {
        Range range = new Range(gt, gte, lt, lte);
        if (range.nonNull()) {
            format(RANGE, column, range);
        }
        return this;
    }

    /**
     * 存在字段
     *
     * @param column 查询字段
     * @return 自己
     */
    public EsQueryWrapper exists(String column) {
        if (isNotEmptyStr(column)) {
            format(EXISTS, column);
        }
        return this;
    }

    /**
     * 地理范围
     *
     * @param column 查询字段
     * @param from   起始半径
     * @param to     结束半径
     * @param lon    经度
     * @param lat    纬度
     * @return 自己
     */
    public EsQueryWrapper distanceRange(String column, Integer from, Integer to, Object lon, Object lat) {
        String lonStr = replaceStr(lon);
        String latStr = replaceStr(lat);
        if (Objects.nonNull(from) && Objects.nonNull(to) && isNotEmptyStr(lonStr) && isNotEmptyStr(latStr)) {
            format(DISTANCE_RANGE, column, from, to, lonStr, latStr);
        }
        return this;
    }

    /**
     * 地理范围
     *
     * @param column 查询字段
     * @param val    坐标数组
     * @return 自己
     */
    public EsQueryWrapper graph(String column, Collection<Point> val) {
        String s = replaceStr(val);
        if (isNotEmptyStr(s)) {
            format(GRAPH, column, s);
        }
        return this;
    }

    /**
     * 将拼接器收集的条件转or关系
     *
     * @return 自己
     */
    public String or() {
        if (isEmpty()) {
            return JSON_EMPTY;
        }
        relationEnum = RelationEnum.AND;
        return String.format(OR, this.toString());
    }

    /**
     * 将拼接器收集的条件转and关系
     *
     * @return 自己
     */
    public String and() {
        if (isEmpty()) {
            return JSON_EMPTY;
        }
        relationEnum = RelationEnum.AND;
        return String.format(AND, this.toString());
    }

    /**
     * 将拼接器收集的条件转not关系
     *
     * @return 自己
     */
    public String not() {
        if (isEmpty()) {
            return JSON_EMPTY;
        }
        relationEnum = RelationEnum.NOT;
        return String.format(NOT, this.toString());
    }

    /**
     * 拼接or子条件
     *
     * @param wrapper 子条件
     * @return 自己
     */
    public EsQueryWrapper or(EsQueryWrapper wrapper) {
        if (Objects.nonNull(wrapper) && wrapper.isNotEmpty()) {
            String or = wrapper.or();
            this.custom(or);
        }
        return this;
    }

    /**
     * 拼接and子条件
     *
     * @param wrapper 子条件
     * @return 自己
     */
    public EsQueryWrapper and(EsQueryWrapper wrapper) {
        if (Objects.nonNull(wrapper) && wrapper.isNotEmpty()) {
            String and = wrapper.and();
            this.custom(and);
        }
        return this;
    }

    /**
     * 拼接and子条件
     *
     * @param wrapper 子条件
     * @return 自己
     */
    public EsQueryWrapper not(EsQueryWrapper wrapper) {
        if (Objects.nonNull(wrapper) && wrapper.isNotEmpty()) {
            String not = wrapper.not();
            this.custom(not);
        }
        return this;
    }

    /**
     * 嵌套or子条件
     *
     * @param consumer 子条件
     * @return 自己
     */
    public EsQueryWrapper or(Consumer<EsQueryWrapper> consumer) {
        if (Objects.isNull(consumer)) {
            return this;
        }
        EsQueryWrapper wrapper = new EsQueryWrapper();
        consumer.accept(wrapper);
        return or(wrapper);
    }

    /**
     * 嵌套and子条件
     *
     * @param consumer 子条件
     * @return 自己
     */
    public EsQueryWrapper and(Consumer<EsQueryWrapper> consumer) {
        if (Objects.isNull(consumer)) {
            return this;
        }
        EsQueryWrapper wrapper = new EsQueryWrapper();
        consumer.accept(wrapper);
        return and(wrapper);
    }

    /**
     * 嵌套not子条件
     *
     * @param consumer 子条件
     * @return 自己
     */
    public EsQueryWrapper not(Consumer<EsQueryWrapper> consumer) {
        if (Objects.isNull(consumer)) {
            return this;
        }
        EsQueryWrapper wrapper = new EsQueryWrapper();
        consumer.accept(wrapper);
        return not(wrapper);
    }

    /**
     * 自定义条件
     *
     * @param query json片段
     * @return 自己
     */
    public EsQueryWrapper custom(String query) {
        if (isNotEmptyStr(query)) {
            this.queryList.add(query);
        }
        return this;
    }

    /**
     * 组装条件
     *
     * @param formatStr 模板
     * @param values    替换值
     */
    private void format(String formatStr, Object... values) {
        if (Objects.nonNull(values) && values.length > 0) {
            this.queryList.add(String.format(formatStr, values));
        }
    }

    /**
     * 替换传入参数格式
     *
     * @param val 值
     * @return 自己
     */
    private String replaceStr(Object val) {
        if (Objects.isNull(val)) {
            return EMPTY;
        }
        String s = JSON.toJSONString(val);
        if (EMPTY.equals(s) || STR_EMPTY.equals(s) || ARRAY_EMPTY.equals(s)) {
            return EMPTY;
        }
        return s;
    }

    public List<String> getQueryList() {
        return this.queryList;
    }

    public void clear() {
        this.queryList.clear();
    }

    public boolean isNotEmpty() {
        return !isEmpty();
    }

    public boolean isEmpty() {
        return this.queryList.isEmpty();
    }

    /**
     * 判断字符串不能为空
     *
     * @param val 值
     * @return 自己
     */
    private static boolean isNotEmptyStr(Object val) {
        return Objects.nonNull(val) && !EMPTY.equals(val);
    }

    @Override
    public String toString() {
        if (isEmpty()) {
            return EMPTY;
        }
        return String.join(",\n", this.queryList);
    }

    /**
     * 构建jsonObject
     *
     * @return JSONObject
     */
    public JSONObject build() {
        String filter;
        if (Objects.isNull(relationEnum)) {
            filter = and();
        } else {
            switch (relationEnum) {
                case OR:
                    filter = or();
                    break;
                case NOT:
                    filter = not();
                    break;
                default:
                    filter = and();
                    break;
            }
        }
        return JSONObject.parseObject(filter, Feature.OrderedField);
    }
}
